import zipfile
import logging
import logging.handlers
import os
import time


def _zip_directory(dir_path, output_file):
    """
    将目标文件夹压缩成zip包，输出到output_file，并将文件删除。
    """
    _zip = zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED)
    for path, dirname, filenames in os.walk(dir_path, topdown=False):
        target_path = path.replace(dir_path, "")
        print(target_path)
        for filename in filenames:
            if os.path.join(path, filename) == output_file:
                continue
            _zip.write(os.path.join(path, filename), os.path.join(target_path, filename))
            os.remove(os.path.join(path, filename))
        if target_path != "":
            os.rmdir(path)
    _zip.close()


def _check_and_create_directory(filename):
    dir_name = os.path.dirname(filename)
    if dir_name == "":
        return
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)


class LoggerManager:
    def __init__(self):
        self.logger_info = dict()  # 用于记录logger的配置信息

    def register(self, logger_name, filename=None, console=True, default_level=logging.INFO, **kwargs):
        """
        注册logger
        :param logger_name: 生成日志实例的名称，唯一标识。

        :param filename: 日志输出的文件名，须为文件的全路径。

        :param console: （默认为True）是否在终端展示日志信息

        :param default_level:（默认为INFO级别）指定全局日志级别

        :param kwargs: 其他可选项：
            `format`： 日志的输出格式，可选项有：
                    - %(asctime)s: 打印日志的时间
                    - %(name)s: 打印logger名称
                    - %(levelname)s: 打印日志级别名称
                    - %(message)s: 打印日志信息
                    - %(levelno)s: 打印日志级别的数值
                    - %(pathname)s: 打印当前执行程序的路径, 其实就是sys.argv[0]
                    - %(filename)s: 打印当前执行程序名
                    - %(funcName)s: 打印日志的当前函数
                    - %(lineno)s: 打印日志的当前行号
                    - %(thread)s: 打印线程ID
                    - %(threadName)s: 打印线程名称
                    - %(process)s:打印进程ID
                    示例：'%(asctime)s - %(name)s -%(lineno)s - %(levelname)s - %(message)s'

            `zip`：(布尔值，默认是False)是否打包压缩日志。
                    如果为真，在解除注册的时候会将生成的所有日志文件打包成压缩文件。

            `size_limit`：日志文件存储大小，单位是M；超过后会创建新的日志文件。
            `max_files`: 最大创建的日志文件数量。
            `mode`:  日志文件的写入模式，默认是"a"

        :return: 返回logger实例
        """
        log_format = kwargs.get("format", None)
        zip_logger = kwargs.get("zip", False)
        file_size_limit = kwargs.get("size_limit", 1024 * 1024)
        max_file = kwargs.get("max_files", None)
        file_mode = kwargs.get("mode", "a")
        for_test = kwargs.get("for_test", False)
        is_test = kwargs.get("is_test", False)

        if log_format is None:
            log_format = "[%(asctime)s][%(name)s]-<thread:%(thread)s>-(line:%(lineno)s), [%(levelname)s]: %(message)s"

        # 获取新的logger实例
        logger = logging.getLogger(logger_name)

        self.logger_info[logger_name] = dict()
        self.logger_info[logger_name]['timestamp'] = time.localtime()
        if filename:
            _check_and_create_directory(filename)
            self.logger_info[logger_name]['file_path'] = os.path.dirname(filename)
            self.logger_info[logger_name]['file_name'] = os.path.basename(filename)
            self.logger_info[logger_name]['zip'] = zip_logger
            self.logger_info[logger_name]["for_test"] = for_test
            self.logger_info[logger_name]["is_test"] = is_test

        # 如果有指定max_file就创建滚动日志实例
        if max_file:
            file_handler = logging.handlers.RotatingFileHandler(
                filename=filename,
                mode=file_mode,
                maxBytes=file_size_limit,
                backupCount=max_file,
                encoding="utf8"
            )
        else:
            # 否则创建普通文件日志实例
            file_handler = logging.FileHandler(filename, mode=file_mode, encoding="utf8")

        # 设置日志格式
        file_handler.setFormatter(logging.Formatter(fmt=log_format))
        logger.addHandler(file_handler)

        if is_test:
            for llogger, lvalue in self.logger_info.items():
                if lvalue["for_test"] and "file_name" in lvalue:
                    logger_filename = os.path.join(os.path.dirname(filename), f"{llogger}.log")
                    case_handler = logging.FileHandler(logger_filename, mode="w", encoding="utf8")
                    case_handler.setFormatter(logging.Formatter(fmt=log_format))
                    lvalue["case_handler"] = case_handler
                    lvalue["logger"].addHandler(case_handler)

        # 判断是否开启终端日志
        if console:
            stream_handle = logging.StreamHandler()
            stream_handle.setFormatter(logging.Formatter(fmt=log_format))
            logger.addHandler(stream_handle)

        # 设置全局日志级别
        logger.setLevel(default_level)

        self.logger_info[logger_name]["logger"] = logger
        return logger

    def unregister(self, logger_name):
        """
        删除注册的logger， 同时将需要打包的logger文件打包
        :param logger_name: 日志实例的名称
        """
        if logger_name in logging.Logger.manager.loggerDict:
            logging.Logger.manager.loggerDict.pop(logger_name)
            # 如果注册了测试用例的Handler,则移除
            if self.logger_info[logger_name]["is_test"]:
                for lloger, lvalue in self.logger_info.items():
                    if "case_handle" in lvalue:
                        lvalue["logger"].removeHandler(lvalue["case_handler"])
                        lvalue.pop("case_handler")
            self._achieve_files(logger_name)
            self.logger_info.pop(logger_name)

    def _achieve_files(self, logger_name):
        if self.logger_info[logger_name]["zip"]:
            current = time.localtime()
            output_file = \
                "achieved_logs_%d_%d_%d_%d_%d_%d.zip" % (
                    current.tm_year, current.tm_mon, current.tm_mday,
                    current.tm_hour, current.tm_min, current.tm_sec
                )
            _zip_directory(self.logger_info[logger_name]["file_path"],
                           os.path.join(self.logger_info[logger_name]["file_path"], output_file))

    def get_logger(self, logger_name):
        """获取日志实例"""
        if logger_name in self.logger_info:
            return self.logger_info[logger_name]["logger"]
        else:
            raise NameError(f"不存在的日志实例{logger_name}")


logger = LoggerManager()
